package com.xy.annotaion.handler.kotlin.compiler.demo1

import com.xy.annotaion.handler.kotlin.p
import java.io.*
import java.util.*
import java.util.regex.Matcher
import java.util.regex.Pattern
import javax.swing.JFileChooser
import javax.swing.JOptionPane
import javax.swing.JScrollPane
import javax.swing.JTextArea


fun main() {

    val l = Lexer(CodeDialog())
    var t: Token
    t = l.read();
    while (t != Token.EOF) {
        p("=> ${t.getText()}")
        t = l.read()
    }

}

open class Lexer(r: Reader) {

    companion object {
        const val regexPat = "\\s*((//.*)|([0-9]+)|(\"(\\\\\"|\\\\\\\\|\\\\n|[^\"])*\")" +
                "|[A-Z_a-z][A-Z_a-z0-9]*|==|<=|>=|&&|\\|\\||\\p{Punct})?"
    }

    private val pattern: Pattern = Pattern.compile(regexPat)
    private val queue = mutableListOf<Token>()
    private var hasMore = false
    var reader: LineNumberReader

    init {
        hasMore = true
        reader = LineNumberReader(r)
    }

    fun peek(i: Int): Token {
        return if (fillQueue(i)) {
            queue[i]
        } else {
            Token.EOF
        }
    }

    private fun fillQueue(i: Int): Boolean {
        while (i >= queue.size) {
            if (hasMore) {
                readLine()
            } else {
                return false
            }
        }
        return true
    }

    private fun readLine() {
        var line = reader.readLine()
        if (Objects.isNull(line)) {
            hasMore = false
            return
        }
        val lineNo = reader.lineNumber;
        val matcher = pattern.matcher(line)
        matcher.useTransparentBounds(true).useAnchoringBounds(false)
        var pos = 0
        var endPos = line.length
        while (pos < endPos) {
            matcher.region(pos, endPos)
            if (matcher.lookingAt()) {
                addToken(lineNo, matcher)
                pos = matcher.end()
            } else {
                throw ParseException("bad token at line $lineNo")
            }
            queue.add(IdToken(lineNo, Token.EOL))
        }
    }

    fun read(): Token {
        if (fillQueue(0)) {
            return queue.removeAt(0)
        } else {
            return Token.EOF
        }
    }

    fun addToken(lineNo: Int, matcher: Matcher) {
        val m = matcher.group(1)
        if (m != null) {
            if (matcher.group(2) == null) {
                var token: Token;
                if (matcher.group(3) != null) {
                    token = NumToken(lineNo, m.toInt())
                } else if (matcher.group(4) != null) {
                    token = StrToken(lineNo, toStringLiteral(m))
                } else {
                    token = IdToken(lineNo, m)
                }
                queue.add(token)

            }
        }
    }

    private fun toStringLiteral(s: String): String {
        val sb = StringBuilder()
        val len = s.length - 1
        var i = 1
        while (i < len) {
            var c = s[i]
            if (c == '\\' && i + 1 < len) {
                val c2 = s[i + 1]
                if (c2 == '"' || c2 == '\\') {
                    c = s[++i]
                } else if (c2 == 'n') {
                    ++i
                    c = '\n'
                }
            }
            i++
            sb.append(c)
        }
        return sb.toString()
    }

}

class IdToken(line: Int, id: String) : Token(line) {
    private val text = id;
    override fun getText() = text
    override fun isIdentifier() = true
}

class StrToken(line: Int, private val literal: String) : Token(line) {
    override fun isString() = true
    override fun getText() = literal
}

class NumToken(line: Int, private val value: Int) : Token(line) {
    override fun getNumber() = value
    override fun isNumber() = true
    override fun getText() = value.toString()
}


class CodeDialog : Reader() {

    private var buffer: String? = null
    private var pos = 0

    override fun read(cbuf: CharArray, off: Int, len: Int): Int {
        if (buffer == null) {
            var `in` = showDialog()
            if (`in` == null) {
                return -1
            } else {
                p(`in`)
                buffer = `in` + '\n'
                pos = 0
            }
        }
        var size = 0
        var length = buffer!!.length
        while (pos < length && size < len) {
            cbuf[off + size++] = buffer!![pos++]
        }
        if (pos == length) {
            buffer = null.toString()
        }
        return size

    }


    override fun close() {
    }


    protected fun print(s: String) = p(s)

    protected fun showDialog(): String? {
        val area = JTextArea(20, 40)
        val pane = JScrollPane(area)
        val result = JOptionPane.showOptionDialog(
            null,
            pane,
            "Input",
            JOptionPane.OK_CANCEL_OPTION,
            JOptionPane.PLAIN_MESSAGE,
            null,
            null,
            null
        )
        if (result == JOptionPane.OK_OPTION) {
            return area.text
        } else {
            return null
        }
    }

    companion object {
        fun file(): Reader {
            val chooser = JFileChooser()
            if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
                return BufferedReader(FileReader(chooser.selectedFile))
            } else {
                throw FileNotFoundException("no file specified")
            }
        }
    }
}

